<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>终端模拟器 - 支持ANSI颜色</title>
    <!-- 引入LayUI CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui@2.6.8/dist/css/layui.min.css">
    <style>
        body {
            background-color: #1E1E1E;
            color: #CCC;
            font-family: 'Courier New', monospace;
            overflow: hidden;
            margin: 0;
            padding: 20px;
        }
        .terminal-container {
            width: 100%;
            height: 95vh;
            box-sizing: border-box;
        }
        .terminal-header {
            background-color: #323233;
            height: 30px;
            border-top-left-radius: 6px;
            border-top-right-radius: 6px;
            display: flex;
            align-items: center;
            padding: 0 15px;
        }
        .terminal-window {
            background-color: #1E1E1E;
            border-radius: 6px;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
            height: calc(100% - 40px);
            overflow: hidden;
        }
        .terminal-body {
            height: calc(100% - 30px);
            padding: 15px;
            overflow-y: auto;
        }
        .terminal-prompt {
            color: #5AD5D5;
            font-weight: bold;
        }
        .terminal-input-line {
            display: flex;
            align-items: center;
        }
        .terminal-input {
            background: transparent;
            border: none;
            color: #FFF;
            font-family: 'Courier New', monospace;
            font-size: 14px;
            outline: none;
            flex: 1;
            padding: 0 5px;
            caret-color: #FFF;
        }
        .terminal-output {
            margin-bottom: 10px;
            white-space: pre-wrap;
            line-height: 1.5;
            font-family: 'Courier New', monospace;
        }
        .red-dot {
            width: 12px;
            height: 12px;
            background-color: #FF5F57;
            border-radius: 50%;
            margin-right: 8px;
        }
        .yellow-dot {
            width: 12px;
            height: 12px;
            background-color: #FFBD2E;
            border-radius: 50%;
            margin-right: 8px;
        }
        .green-dot {
            width: 12px;
            height: 12px;
            background-color: #28CA42;
            border-radius: 50%;
            margin-right: 8px;
        }
        
        /* ANSI颜色样式 */
        .ansi-bold { font-weight: bold; }
        .ansi-italic { font-style: italic; }
        .ansi-underline { text-decoration: underline; }
        .ansi-inverse { filter: invert(1); }
        .ansi-strikethrough { text-decoration: line-through; }
        
        /* 前景色 */
        .ansi-fg-black { color: #000; }
        .ansi-fg-red { color: #E54B4B; }
        .ansi-fg-green { color: #9ECE58; }
        .ansi-fg-yellow { color: #FAED70; }
        .ansi-fg-blue { color: #7AA5FF; }
        .ansi-fg-magenta { color: #BB80B3; }
        .ansi-fg-cyan { color: #7DCFFF; }
        .ansi-fg-white { color: #D0D0D0; }
        
        /* 亮前景色 */
        .ansi-fg-bright-black { color: #808080; }
        .ansi-fg-bright-red { color: #FF5370; }
        .ansi-fg-bright-green { color: #C3E88D; }
        .ansi-fg-bright-yellow { color: #FFCB6B; }
        .ansi-fg-bright-blue { color: #82AAFF; }
        .ansi-fg-bright-magenta { color: #C792EA; }
        .ansi-fg-bright-cyan { color: #89DDFF; }
        .ansi-fg-bright-white { color: #FFFFFF; }
        
        /* 背景色 */
        .ansi-bg-black { background-color: #000; }
        .ansi-bg-red { background-color: #E54B4B; }
        .ansi-bg-green { background-color: #9ECE58; }
        .ansi-bg-yellow { background-color: #FAED70; }
        .ansi-bg-blue { background-color: #7AA5FF; }
        .ansi-bg-magenta { background-color: #BB80B3; }
        .ansi-bg-cyan { background-color: #7DCFFF; }
        .ansi-bg-white { background-color: #D0D0D0; }
        
        /* 亮背景色 */
        .ansi-bg-bright-black { background-color: #808080; }
        .ansi-bg-bright-red { background-color: #FF5370; }
        .ansi-bg-bright-green { background-color: #C3E88D; }
        .ansi-bg-bright-yellow { background-color: #FFCB6B; }
        .ansi-bg-bright-blue { background-color: #82AAFF; }
        .ansi-bg-bright-magenta { background-color: #C792EA; }
        .ansi-bg-bright-cyan { background-color: #89DDFF; }
        .ansi-bg-bright-white { background-color: #FFFFFF; }
    </style>
</head>
<body>
    <div class="terminal-container">
        <div class="terminal-window">
            <div class="terminal-header">
                <div class="red-dot"></div>
                <div class="yellow-dot"></div>
                <div class="green-dot"></div>
                <span style="color: #999; font-size: 14px;">bash</span>
            </div>
            <div class="terminal-body" id="terminalBody">
                <div class="terminal-output">欢迎使用终端模拟器 v2.0 - 支持ANSI颜色显示</div>
                <div class="terminal-output">输入 'help' 查看可用命令</div>
                <div class="terminal-output"></div>
            </div>
        </div>
    </div>

    <!-- 引入LayUI和SocketIO -->
    <script src="https://cdn.jsdelivr.net/npm/layui@2.6.8/dist/layui.min.js"></script>
    <script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>
    
    <script>
        class Terminal {
            constructor() {
                this.history = [];
                this.historyIndex = -1;
                this.currentInput = '';
                this.socket = null; // SocketIO连接对象
                this.isConnected = false;
                
                this.init();
            }
            
            // 初始化终端
            init() {
                this.terminalBody = document.getElementById('terminalBody');
                this.renderInputLine();
                this.focusInput();
                this.setupEventListeners();
                
                // 初始化SocketIO连接 - 预留位置
                // this.initSocket();
                
                // 显示示例输出
                this.showExampleOutput();
            }
            
            // 显示示例输出（包含ANSI颜色代码）
            showExampleOutput() {
                const exampleOutput = `Last login: Wed Aug 27 14:14:34 2025 from 127.0.0.1

-bash: /home/ros/Downloads/.ota/untar/common/bash/obf_em.sh: No such file or directory
\x1b[33mSet MF_SYSTEM_ROOT_DIR = /mnt/data/mf_system \x1b[0m
root@chengchen:~# ls -la
total 107568
drwx------ 25 root root      4096 Aug 27 12:03 \x1b[0m\x1b[01;34m.\x1b[0m
drwxr-xr-x 21 root root      4096 Aug 26 17:31 \x1b[01;34m..\x1b[0m
drwxr-xr-x 31 root root      4096 Jun 26 15:57 \x1b[01;34manaconda3\x1b[0m
-rw-------  1 root root     38506 Aug 27 12:03 .bash_history
-rw-r--r--  1 root root      3785 Aug 20 10:27 .bashrc
-rw-r--r--  1 root root      3785 Aug 20 10:27 .bashrc_bak
-rw-r--r--  1 root root     16384 Jul  4 13:16 .bashrc.swp
drwx------ 12 root root      4096 Aug 26 10:37 \x1b[01;34m.cache\x1b[0m
-rwxr-xr-x  1 root root       786 Jul 17 21:44 \x1b[01;32mclean_logs.sh\x1b[0m
-rw-------  1 root root         0 Jul 16 16:05 .clean_logs.sh.swp
drwxr-xr-x  2 root root      4096 Jun 26 16:00 \x1b[01;34m.conda\x1b[0m
-rw-r--r--  1 root root        26 Jun 26 15:59 .condarc
drwx------ 13 root root      4096 Jul  2 19:56 \x1b[01;34m.config\x1b[0m
drwx------  3 root root      4096 Oct 17  2023 \x1b[01;34m.dbus\x1b[0m
drwxr-xr-x  2 root root      4096 Aug  5 10:49 \x1b[01;34mDesktop\x1b[0m
drwxr-xr-x  6 root root      4096 Aug  7 15:19 \x1b[01;34mDocuments\x1b[0m
drwxr-xr-x  2 root root      4096 Jun 26 15:54 \x1b[01;34mDownloads\x1b[0m
-rw-r--r--  1 root root        57 Aug  7 14:12 .gitconfig
drwx------  3 root root      4096 Jul  2 20:10 \x1b[01;34m.gnupg\x1b[0m
drwxr-xr-x  4 root root      4096 Jul  2 20:08 \x1b[01;34m.java\x1b[0m
drwx------  3 root root      4096 Oct 17  2023 \x1b[01;34m.local\x1b[0m
drwxr-xr-x  5 root root      4096 Jul 18 20:30 \x1b[01;34m.marscode\x1b[0m
drwx------  4 root root      4096 Jun 26 14:58 \x1b[01;34m.mozilla\x1b[0m
drwxr-xr-x  2 root root      4096 Oct 17  2023 \x1b[01;34mMusic\x1b[0m
drwxr-xr-x  2 root root      4096 Oct 17  2023 \x1b[01;34mPictures\x1b[0m
-rw-r--r--  1 root root       161 Dec  5  2019 .profile
drwxr-xr-x  2 root root      4096 Oct 17  2023 \x1b[01;34mPublic\x1b[0m
drwx------  3 root root      4096 Oct 17  2023 \x1b[01;34msnap\x1b[0m
drwxr-xr-x  2 root root      4096 Aug 25 15:36 \x1b[01;34m.ssh\x1b[0m
drwxr-xr-x  2 root root      4096 Oct 17  2023 \x1b[01;34mTemplates\x1b[0m
-rw-r--r--  1 root root 109887388 Jul 10 14:58 tmp
-rw-r--r--  1 root root        33 Aug 25 17:38 tools
drwxr-xr-x  2 root root      4096 Oct 17  2023 \x1b[01;34mVideos\x1b[0m
-rw-------  1 root root     12198 Aug 26 17:18 .viminfo
drwx------  2 root root      4096 Aug 20 07:03 \x1b[01;34m.vnc\x1b[0m
drwxr-xr-x  3 root root      4096 Aug  5 18:11 \x1b[01;34m.vscode-remote-containers\x1b[0m
drwxr-x---  5 root root      4096 Aug 27 00:02 \x1b[01;34m.vscode-server\x1b[0m
-rw-r--r--  1 root root       236 Aug 18 11:11 .wget-hsts
-rw-------  1 root root       206 Aug 20 07:03 .Xauthority
-rw-------  1 root root     44643 Aug 20 07:06 .xsession-errors
root@chengchen:~# `;
                
                // 使用setTimeout模拟逐步输出
                const lines = exampleOutput.split('\n');
                let index = 0;
                
                const outputNextLine = () => {
                    if (index < lines.length) {
                        // 将ANSI颜色代码转换为HTML
                        const formattedLine = this.ansiToHtml(lines[index]);
                        this.printOutput(formattedLine);
                        index++;
                        setTimeout(outputNextLine, 50);
                    } else {
                        this.renderInputLine();
                    }
                };
                
                outputNextLine();
            }
            
            // 初始化SocketIO连接 - 预留位置
            initSocket() {
                /*
                实际实现示例:
                this.socket = io('http://localhost:5000');
                
                this.socket.on('connect', () => {
                    this.isConnected = true;
                    this.printOutput('已连接到服务器');
                });
                
                this.socket.on('disconnect', () => {
                    this.isConnected = false;
                    this.printOutput('与服务器的连接已断开');
                });
                
                this.socket.on('command_result', (data) => {
                    // 将ANSI颜色代码转换为HTML
                    const formattedOutput = this.ansiToHtml(data.result);
                    this.printOutput(formattedOutput);
                    this.renderInputLine();
                });
                */
                
                // 模拟连接成功
                setTimeout(() => {
                    this.isConnected = true;
                    this.printOutput('已连接到服务器 (模拟)');
                }, 500);
            }
            
            // 设置事件监听
            setupEventListeners() {
                document.addEventListener('click', () => {
                    this.focusInput();
                });
                
                document.addEventListener('keydown', (e) => {
                    this.handleKeyDown(e);
                });
            }
            
            // 处理按键事件
            handleKeyDown(e) {
                const input = document.getElementById('terminalInput');
                
                if (e.key === 'Enter') {
                    this.executeCommand(input.value);
                } else if (e.key === 'ArrowUp') {
                    e.preventDefault();
                    this.navigateHistory('up');
                } else if (e.key === 'ArrowDown') {
                    e.preventDefault();
                    this.navigateHistory('down');
                } else if (e.key === 'Tab') {
                    e.preventDefault();
                    // 预留Tab补全功能
                }
            }
            
            // 执行命令
            executeCommand(command) {
                if (!command.trim()) {
                    this.renderInputLine();
                    return;
                }
                
                // 显示输入的命令
                this.printOutput(this.getPrompt() + command);
                
                // 添加到历史记录
                this.history.push(command);
                this.historyIndex = this.history.length;
                
                // 处理特殊命令
                if (command === 'clear' || command === 'cls') {
                    this.clearTerminal();
                    this.renderInputLine();
                    return;
                }
                
                // 发送命令到后端 - 预留位置
                this.sendCommandToBackend(command);
            }
            
            // 发送命令到后端 - 预留位置
            sendCommandToBackend(command) {
                /*
                实际实现示例:
                if (this.isConnected) {
                    this.socket.emit('execute_command', { command: command });
                } else {
                    this.printOutput('错误: 未连接到服务器');
                    this.renderInputLine();
                }
                */
                
                // 模拟后端响应
                setTimeout(() => {
                    this.printOutput(`命令已发送: ${command} (模拟后端响应)`);
                    this.renderInputLine();
                }, 300);
            }
            
            // 在历史记录中导航
            navigateHistory(direction) {
                if (this.history.length === 0) return;
                
                if (direction === 'up') {
                    if (this.historyIndex > 0) {
                        this.historyIndex--;
                    }
                } else {
                    if (this.historyIndex < this.history.length - 1) {
                        this.historyIndex++;
                    } else {
                        this.historyIndex = this.history.length;
                        document.getElementById('terminalInput').value = this.currentInput;
                        return;
                    }
                }
                
                document.getElementById('terminalInput').value = this.history[this.historyIndex];
            }
            
            // 获取提示符
            getPrompt() {
                return '<span class="terminal-prompt">user@terminal:~$ </span>';
            }
            
            // 渲染输入行
            renderInputLine() {
                // 移除现有的输入行
                const existingInputLine = document.getElementById('terminalInputLine');
                if (existingInputLine) {
                    existingInputLine.remove();
                }
                
                // 创建新的输入行
                const inputLine = document.createElement('div');
                inputLine.id = 'terminalInputLine';
                inputLine.className = 'terminal-input-line';
                inputLine.innerHTML = `
                    ${this.getPrompt()}
                    <input type="text" id="terminalInput" class="terminal-input" autocomplete="off">
                `;
                
                this.terminalBody.appendChild(inputLine);
                this.focusInput();
                
                // 保存当前输入内容
                const input = document.getElementById('terminalInput');
                input.addEventListener('input', () => {
                    this.currentInput = input.value;
                });
                
                // 滚动到底部
                this.scrollToBottom();
            }
            
            // 输出内容到终端
            printOutput(content) {
                const output = document.createElement('div');
                output.className = 'terminal-output';
                
                // 如果内容不是HTML，直接设置为文本内容
                if (content.includes('<') && content.includes('>')) {
                    output.innerHTML = content;
                } else {
                    output.textContent = content;
                }
                
                // 在输入行之前插入输出
                const inputLine = document.getElementById('terminalInputLine');
                if (inputLine) {
                    this.terminalBody.insertBefore(output, inputLine);
                } else {
                    this.terminalBody.appendChild(output);
                }
                
                this.scrollToBottom();
            }
            
            // 清空终端
            clearTerminal() {
                while (this.terminalBody.firstChild) {
                    this.terminalBody.removeChild(this.terminalBody.firstChild);
                }
            }
            
            // 聚焦到输入框
            focusInput() {
                const input = document.getElementById('terminalInput');
                if (input) {
                    input.focus();
                    
                    // 确保光标在文本末尾
                    const len = input.value.length;
                    input.setSelectionRange(len, len);
                }
            }
            
            // 滚动到底部
            scrollToBottom() {
                this.terminalBody.scrollTop = this.terminalBody.scrollHeight;
            }
            
            // 将ANSI颜色代码转换为HTML
            ansiToHtml(text) {
                // 简化版的ANSI转HTML转换器
                // 实际实现可能需要更复杂的解析
                return text
                    .replace(/\x1b\[1m/g, '<span class="ansi-bold">')
                    .replace(/\x1b\[3m/g, '<span class="ansi-italic">')
                    .replace(/\x1b\[4m/g, '<span class="ansi-underline">')
                    .replace(/\x1b\[7m/g, '<span class="ansi-inverse">')
                    .replace(/\x1b\[9m/g, '<span class="ansi-strikethrough">')
                    .replace(/\x1b\[0m/g, '</span>')
                    .replace(/\x1b\[30m/g, '<span class="ansi-fg-black">')
                    .replace(/\x1b\[31m/g, '<span class="ansi-fg-red">')
                    .replace(/\x1b\[32m/g, '<span class="ansi-fg-green">')
                    .replace(/\x1b\[33m/g, '<span class="ansi-fg-yellow">')
                    .replace(/\x1b\[34m/g, '<span class="ansi-fg-blue">')
                    .replace(/\x1b\[35m/g, '<span class="ansi-fg-magenta">')
                    .replace(/\x1b\[36m/g, '<span class="ansi-fg-cyan">')
                    .replace(/\x1b\[37m/g, '<span class="ansi-fg-white">')
                    .replace(/\x1b\[90m/g, '<span class="ansi-fg-bright-black">')
                    .replace(/\x1b\[91m/g, '<span class="ansi-fg-bright-red">')
                    .replace(/\x1b\[92m/g, '<span class="ansi-fg-bright-green">')
                    .replace(/\x1b\[93m/g, '<span class="ansi-fg-bright-yellow">')
                    .replace(/\x1b\[94m/g, '<span class="ansi-fg-bright-blue">')
                    .replace(/\x1b\[95m/g, '<span class="ansi-fg-bright-magenta">')
                    .replace(/\x1b\[96m/g, '<span class="ansi-fg-bright-cyan">')
                    .replace(/\x1b\[97m/g, '<span class="ansi-fg-bright-white">')
                    .replace(/\x1b\[40m/g, '<span class="ansi-bg-black">')
                    .replace(/\x1b\[41m/g, '<span class="ansi-bg-red">')
                    .replace(/\x1b\[42m/g, '<span class="ansi-bg-green">')
                    .replace(/\x1b\[43m/g, '<span class="ansi-bg-yellow">')
                    .replace(/\x1b\[44m/g, '<span class="ansi-bg-blue">')
                    .replace(/\x1b\[45m/g, '<span class="ansi-bg-magenta">')
                    .replace(/\x1b\[46m/g, '<span class="ansi-bg-cyan">')
                    .replace(/\x1b\[47m/g, '<span class="ansi-bg-white">')
                    .replace(/\x1b\[100m/g, '<span class="ansi-bg-bright-black">')
                    .replace(/\x1b\[101m/g, '<span class="ansi-bg-bright-red">')
                    .replace(/\x1b\[102m/g, '<span class="ansi-bg-bright-green">')
                    .replace(/\x1b\[103m/g, '<span class="ansi-bg-bright-yellow">')
                    .replace(/\x1b\[104m/g, '<span class="ansi-bg-bright-blue">')
                    .replace(/\x1b\[105m/g, '<span class="ansi-bg-bright-magenta">')
                    .replace(/\x1b\[106m/g, '<span class="ansi-bg-bright-cyan">')
                    .replace(/\x1b\[107m/g, '<span class="ansi-bg-bright-white">')
                    .replace(/\x1b\[0;(\d+);(\d+)m/g, '<span class="ansi-fg-$1 ansi-bg-$2">')
                    .replace(/\x1b\[1;(\d+)m/g, '<span class="ansi-bold ansi-fg-$1">')
                    .replace(/\x1b\[(\d+);(\d+);(\d+)m/g, '<span class="ansi-fg-$1 ansi-bg-$2 ansi-$3">');
            }
        }
        
        // 页面加载完成后初始化终端
        document.addEventListener('DOMContentLoaded', () => {
            window.terminal = new Terminal();
        });
    </script>
</body>
</html>